package com.sunny.springboot2.core.lock;

import com.sunny.springboot2.core.annotation.CacheLock;
import com.sunny.springboot2.core.annotation.CacheLockParam;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * 通过接口注入的方式去写不同的生成规则;
 * @author sunny
 * @date 2018/11/29
 */
@Component
public class LockKeyGenerator implements CacheKeyGenerator {

    @Override
    public String getLockKey(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        CacheLock lockAnnotation = method.getAnnotation(CacheLock.class);

        Object[] args = joinPoint.getArgs();
        Parameter[] parameters = method.getParameters();
        StringBuilder sb = new StringBuilder();
        //TODO 默认解析方法里带 CacheParam 注解的属性， 如果没有尝试解析实体对象中带
        for (int i = 0; i < parameters.length; i++) {
            final CacheLockParam annotation = parameters[i].getAnnotation(CacheLockParam.class);
            if (annotation == null) {
                continue;
            }
            sb.append(lockAnnotation.delimiter()).append(args[i]);
        }
        if (StringUtils.isBlank(sb.toString())) {
            final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                final Object obj = args[i];
                final Field[] fields = obj.getClass().getDeclaredFields();
                for (Field field : fields) {
                    final CacheLockParam annotation = field.getAnnotation(CacheLockParam.class);
                    if (annotation == null) {
                        continue;
                    }
                    field.setAccessible(true);
                    sb.append(lockAnnotation.delimiter()).append(ReflectionUtils.getField(field, obj));
                }
            }
        }
        return lockAnnotation.prefix() + sb.toString();
    }
}
